import { LongBits } from "./LongBits";

const longBits = new LongBits(0, 0);

export type KWriteOpFn = (buf: Uint8Array, pos: number, v: any) => number;

/**
 * 二进制写入操作
 */
export class KWriteOp {

    fn: KWriteOpFn;
    val: any;
    nxt: KWriteOp;

    constructor(fn: KWriteOpFn, val: any) {
        this.fn = fn;
        this.val = val;
    }

    static empty(buf: Uint8Array, pos: number, v: number) {
        return pos;
    }

    static byte(buf: Uint8Array, pos: number, v: number) {
        buf[pos++] = v;
        return pos;
    }

    static bytes(buf: Uint8Array, pos: number, v: Uint8Array) {
        for (let i = 0; i < v.length; i++) {
            buf[pos++] = v[i];
        }
        return pos;
    }

    static uint8(buf: Uint8Array, pos: number, v: number) {
        buf[pos++] = v >>> 0 & 255;
        return pos;
    }

    static uint16(buf: Uint8Array, pos: number, v: number) {
        buf[pos++] = v >>> 0 & 255;
        buf[pos++] = v >>> 8 & 255;
        return pos;
    }

    static uint32(buf: Uint8Array, pos: number, v: number) {
        buf[pos++] = v >>> 0 & 255;
        buf[pos++] = v >>> 8 & 255;
        buf[pos++] = v >>> 16 & 255;
        buf[pos++] = v >>> 24 & 255;
        return pos;
    }

    static uint64(buf: Uint8Array, pos: number, v: number) {
        let lo = 0;
        let hi = 0;
        if (v > 0) {
            lo = v >>> 0;
            hi = (v - lo) / 4294967296 >>> 0;
        }
        buf[pos++] = lo >>> 0 & 255;
        buf[pos++] = lo >>> 8 & 255;
        buf[pos++] = lo >>> 16 & 255;
        buf[pos++] = lo >>> 24 & 255;
        buf[pos++] = hi >>> 0 & 255;
        buf[pos++] = hi >>> 8 & 255;
        buf[pos++] = hi >>> 16 & 255;
        buf[pos++] = hi >>> 24 & 255;
        return pos;
    }

    static auint32(buf: Uint8Array, pos: number, v: number) {
        while (v > 127) {
            buf[pos++] = (v & 127) | 128;
            v >>>= 7;
        }
        buf[pos++] = v;
        return pos;
    }

    static auint64(buf: Uint8Array, pos: number, v: LongBits) {
        while (v.hi) {
            buf[pos++] = v.lo & 127 | 128;
            v.lo = (v.lo >>> 7 | v.hi << 25) >>> 0;
            v.hi >>>= 7;
        }
        while (v.lo > 127) {
            buf[pos++] = v.lo & 127 | 128;
            v.lo = v.lo >>> 7;
        }
        buf[pos++] = v.lo;
        return pos;
    }

    static int8(buf: Uint8Array, pos: number, v: number) {
        return KWriteOp.uint8(buf, pos, v);
    }

    static int16(buf: Uint8Array, pos: number, v: number) {
        return KWriteOp.uint16(buf, pos, v);
    }

    static int32(buf: Uint8Array, pos: number, v: number) {
        return KWriteOp.uint32(buf, pos, v);
    }

    static int64(buf: Uint8Array, pos: number, v: number) {
        let bits = LongBits.from(v, longBits);
        pos = KWriteOp.int32(buf, pos, bits.lo);
        pos = KWriteOp.int32(buf, pos, bits.hi);
        return pos;
    }

    static float32(buf: Uint8Array, pos: number, v: number) {
        let sign = ((v < 0) ? 1 : 0) << 31;
        v = sign ? -v : v;

        // 正0:负0
        if (v === 0) {
            return KWriteOp.uint32(buf, pos, (1 / v) > 0 ? 0 : 204800000);
        }

        // 非数字
        if (isNaN(v)) {
            return KWriteOp.uint32(buf, pos, 2147483647);
        }

        // 无穷数
        if (v > 3.4028234663852886e+38) {
            return KWriteOp.uint32(buf, pos, (sign | (2139095040)) >>> 0);
        }

        // 非正规数
        if (v < 1.1754943508222875e-38) {
            let mant = Math.round(v / 1.401298464324817e-45);
            return KWriteOp.uint32(buf, pos, (sign | mant) >>> 0);
        }

        let exp = Math.floor(Math.log(v) / Math.LN2);
        let mant = v * Math.pow(2, -exp);
        mant = Math.round(mant * 8388608) & 8388607;

        return KWriteOp.uint32(buf, pos, (sign | ((exp + 127) << 23) | mant) >>> 0);
    }

    static str(buf: Uint8Array, pos: number, v: string) {
        for (let i = 0; i < v.length; i++) {
            const c = v.charCodeAt(i);
            if (c < 128) {
                buf[pos++] = c;
            } else if (c < 2048) {
                buf[pos++] = 192 | (31 & (c >> 6));
                buf[pos++] = 128 | (63 & c);
            } else if (c < 65536) {
                buf[pos++] = 224 | (15 & (c >> 12));
                buf[pos++] = 128 | (63 & (c >> 6));
                buf[pos++] = 128 | (63 & c);
            } else {
                buf[pos++] = 248 | (3 & (c >> 18));
                buf[pos++] = 128 | (63 & (c >> 12));
                buf[pos++] = 128 | (63 & (c >> 6));
                buf[pos++] = 128 | (63 & c);
            }
        }
        return pos;
    }

    static strs(buf: Uint8Array, pos: number, v: string[]) {
        pos = KWriteOp.auint32(buf, pos, v.length);
        for (let i = 0; i < v.length; ++i) {
            const str = v[i];
            pos = KWriteOp.auint32(buf, pos, str.length);
            pos = KWriteOp.str(buf, pos, str);
        }
        return pos;
    }
}